package goroutine_id

import (
	"runtime"
	"strings"
	"sync"
	"unsafe"
)

var offsetDictMap = map[string]uintptr{
	"go1.10": 152,
	"go1.20": 152,
	"go1.9":  152,
	"go1.8":  192,
}

var goidOffset = func() uintptr {
	goVersion := runtime.Version()
	for key, off := range offsetDictMap {
		if goVersion == key || strings.HasPrefix(goVersion, key) {
			return off
		}
	}
	panic("unsupport go verion:" + goVersion)
}()

func getg() unsafe.Pointer

func goidFromPtr(gptr unsafe.Pointer) int64 {
	// ARM64中g结构体中goid字段的偏移量可能会变化,请查阅官方文档
	//const goidOffset = 0x8

	return *(*int64)(unsafe.Pointer(uintptr(gptr) + goidOffset))
}

var gls struct {
	m map[int64]map[interface{}]interface{}
	sync.Mutex
}

func init() {
	gls.m = make(map[int64]map[interface{}]interface{})
}

func getMap() map[interface{}]interface{} {
	gls.Lock()
	defer gls.Unlock()

	goid := GetGoid()
	if m, _ := gls.m[goid]; m != nil {
		return m
	}

	m := make(map[interface{}]interface{})
	gls.m[goid] = m
	return m
}

func Get(key interface{}) interface{} {
	return getMap()[key]
}

func Put(key interface{}, v interface{}) {
	getMap()[key] = v
}

func Delete(key interface{}) {
	delete(getMap(), key)
}

func Clean() {
	gls.Lock()
	defer gls.Unlock()

	delete(gls.m, GetGoid())
}

func GetGoid() int64 {
	return goidFromPtr(getg())
}
